package com.qf.modules.sys.service;

import com.qf.common.exception.BusinessException;
import com.qf.common.persistence.Page;
import com.qf.common.service.CrudService;
import com.qf.common.util.*;
import com.qf.common.util.VerificationCodeUtils;
import com.qf.modules.sys.entity.User;
import com.qf.modules.sys.mapper.UserDao;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserService extends CrudService<UserDao, User> {
    @Autowired
    private PasswordUtils passwordUtils;
    @Autowired
    private RedisTemplateUtils redisTemplate;

    public List<User> findList(User user) {
        return super.findList(user);
    }

    public Page<User> findPage(Page<User> page,User user) {
        return super.findPage(page,user);
    }

    @Transactional(readOnly = false)
    public ResultInfo login(User user) {
        if (!verifyImageCode(user.getCode())) {
            return ResultInfo.error("验证码错误!");
        }

        // 调用后端服务进行登录操作。该服务将验证用户凭据。
        List<User> userList = dao.findList(user);

        // 登录失败
        if (userList.isEmpty()) {
            return ResultInfo.error("账号或密码有误!");
        }

        User userInfo = userList.get(0);

        if (!passwordUtils.verify(user.getPassword(), userInfo.getPassword())) {
            return ResultInfo.error("账号或密码有误!");
        }

        // 登录成功后，准备生成JWT令牌。首先创建一个用于存放令牌信息的HashMap。
        HashMap<String, Object> map = new HashMap<>();
        map.put("userId", userInfo.getId());

        // 调用后端服务的getJwtToken方法，生成JWT令牌。
        String jwtToken =  getJwtToken(map);

        // 将生成的JWT令牌设置到登录用户信息对象（LoginUserVo）中。
        userInfo.setToken(jwtToken);

        return ResultInfo.ok().setData(userInfo);
    }

    /**
     * 获取token
     * @param map 内容
     */
    private String getJwtToken(HashMap<String, Object> map){
        String jwtToken = JwtUtils.createJWT(AdminUtils.JWT_KEY, AdminUtils.JWT_TTL, map);
        //拿到用户的id
        String userId = map.get("userId").toString();
        redisTemplate.set(AdminUtils.JWT_KEY + "-" + userId, jwtToken, AdminUtils.JWT_TTL, TimeUnit.MILLISECONDS);
        return jwtToken;
    }

    @Transactional(readOnly = false)
    public ResultInfo register(User user) {
        //判断是否用户名已存在
        List<User> userList = dao.findList(new User(){{
            setLoginName(user.getLoginName());
        }});

        if (userList.isEmpty()) {
            return ResultInfo.error("用户名已有人使用，请重新定义！");
        }

        //密码加密
        String encryptPassword = passwordUtils.encrypt(user.getPassword());
        user.setPassword(encryptPassword);
        user.setId(IdGen.uuid());
        user.setIsNewRecord(true);
        save(user);

        return ResultInfo.ok();
    }

    @Transactional(readOnly = false)
    public void save(User user){
        super.save(user);
    }

    @Transactional(readOnly = false)
    public ResultInfo saveWithVerify(User user) {
        if (user.getIsNewRecord()) {
            return register(user);
        }else {
            save(user);
        }
        return ResultInfo.ok();
    }

    /**
     * 获取图形验证码
     * @return  返回图片流
     */
    public ResultInfo getImageCode() {
        String code = VerificationCodeUtils.generateVerifyCode(4);

        redisTemplate.set("validate_code",code,2,TimeUnit.MINUTES);

        // 字节数组输出流在内存中创建一个字节数组缓冲区，所有发送到输出流的数据保存在该字节数组缓冲区中
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        //将得到的验证码，使用工具类生成验证码图片，并放入到字节数组缓存区
        try {
            VerificationCodeUtils.outputImage(220,80,byteArrayOutputStream,code);
        } catch (IOException e) {
            throw new BusinessException("图形验证码缓冲异常！");
        }
        //使用spring提供的工具类，将字节缓存数组中的验证码图片流转换成Base64的形式
        //并返回给浏览器
        Base64 base64 = new Base64();
        String imageCode = "data:image/png;base64," + base64.encodeToString(byteArrayOutputStream.toByteArray());
        return ResultInfo.ok().setData(imageCode);
    }

    /**
     * 验证图形验证码
     * @param code  图形验证码
     */
    public Boolean verifyImageCode(String code) {
        Object validateCode = redisTemplate.get("validate_code");
        if (validateCode == null) {
            return false;
        }
        return code.equalsIgnoreCase(validateCode.toString());
    }
}
